#ifndef Algebra_LinearSolver_h
#define Algebra_LinearSolver_h

#include "AlgebraModule.h"
#include "AlgebraicTypes.h"
#include "ElapsedTimer.h"
#include "Object.h"

// #include "Solver/Framework/Assembly/include/Assembly.h"

SG_NAMESPACE_OPEN
namespace Algebra
{
    /**
     * @defgroup LinearSolver Linear Solver
     * Solve sparse linear systems systems of equations.
     */
    ///@{

    /**
     * @brief Abstract class to define the interfaces for solving sparse linear systems of equations.
     *
     * From the point of mine, solving systems of linear equations is one of the three most
     * important topics in the traditional/numerical linear algebra. The other two are least
     * squares problems, eigenvalue problems.
     *
     * @see https://netlib.org/scalapack/
     *
     * In FEM analysis, usually, the assembly stiffness matrix is a sparse matrix, where
     * only a few coefficients are different from zero. In such cases, memory consumption
     * can be reduced and performance increased by using a specialized representation
     * storing only the nonzero coefficients.
     *
     * @see https://eigen.tuxfamily.org/index.php?title=Main_Page
     *
     * Thus, solving sparse linear systems of equations plays a important role in the
     * numerical simulations.
     *
     * The ﬁrst requirement for linear solvers is to be interchangeable with each other.
     * While there are no best solver for all cases and in general choosing the proper solver
     * depends also to the problem it has to solve, it is highly desirable for users to change
     * from one solver to other in order to ﬁnd the best one for their case. This feature can
     * be provided using the Strategy pattern.
     *
     * @see Dadvand P. A framework for developing finite element codes for multi-disciplinary applications.
     *
     * To support data types other than double, such as float, float complex, double complex,
     * it seems that LinearSolver should be designed as a class template.
     *
     * @author nene
     * @date July, 2024.
     *
     */
    template <typename TDataType>
    class ALGEBRA_EXPORT TLinearSolver : public Base::Object
    {
      public:
        ///@name Type Definitions
        ///@{
        typedef /*typename*/ TDataType                 DataType;
        typedef /*typename*/ Vector_t<TDataType>       VectorType;
        typedef /*typename*/ Matrix_t<TDataType>       MatrixType;
        typedef /*typename*/ SparseMatrix_t<TDataType> SparseMatrixType;
        ///@}

        TLinearSolver ();
        virtual ~TLinearSolver ();

        // return the class name as a string.
        virtual const char* getClassName () = 0;

        /// Initialize the execution environment
        virtual bool initialize ()
        {
            return true;
        };
        /// Terminate the execution environment.
        virtual bool finalize ()
        {
            return true;
        };

        /// initialize the solver with the matrix A for further solving Ax=b problems.
        virtual void compute (const SparseMatrixType& matrix) = 0;

        /// evaluate the solution x of Ax=b
        virtual void solve (const VectorType& b, VectorType& x) const = 0;

        virtual void solve (const SparseMatrixType& b, SparseMatrixType& x) const = 0;

        virtual Info_t info () const = 0;

      protected:
        bool m_isInitialized = false;
    };

    // Enforce the explicit instantiation to hide the underlying implementation.
    template class ALGEBRA_EXPORT TLinearSolver<Real_t>;
    template class ALGEBRA_EXPORT TLinearSolver<Complex_t>;

    // Decalrations for the explicit specializations of the members.
    template <>
    ALGEBRA_EXPORT TLinearSolver<Real_t>::TLinearSolver ();
    template <>
    ALGEBRA_EXPORT TLinearSolver<Real_t>::~TLinearSolver ();

    template <>
    ALGEBRA_EXPORT TLinearSolver<Complex_t>::TLinearSolver ();
    template <>
    ALGEBRA_EXPORT TLinearSolver<Complex_t>::~TLinearSolver ();

    // alias for compatibilities with the external modules
    typedef TLinearSolver<Real_t> LinearSolver;

    ///@}
}  // namespace Algebra

SG_NAMESPACE_CLOSE

#endif  // SG_Algebra_LinearSolver_h